module.exports = svg2gcode;

var GCanvas = require('./gcanvas'); // gcode 生成画布
var canvg = require('./canvg'); // svg 处理库
var GCodeDriver = require('./drivers/gcode');

// interface IDeviceConfig {
//     checkLaserParams: {
//       offsetX: number
//       offsetY: number
//       isMirror_Y: boolean
//       isMirror_X: boolean
//       secondReverse: boolean
//     }
//     checkRedLightParams: {
//       horizontalOffset: number
//       verticalOffset: number
//     }
//   }

// fillParams = {
//   useFill: ture,
//   strokeFirst: true, // 轮廓优先，默认先加工轮廓，后加工填充
//   style: 2, // 填充 模式，2为水平双向
//   aroundEdgeOnce: false, // 绕边走一次
//   crossFill: false, // 交叉填充
//   angle: 0, // 填充角度
//   lineSpacing: 0.05, // 填充线间距
//   evenDistribution: true, // 均匀分布填充线
//   margins： 0, // 填充边距
//   beginOffset: 0, // 开始偏移
//   endOffset: 0, // 结束偏移
//   lineIndent: 0, // 填充线缩进
//   boundaryLoopsNum: 0, // 边界环数
//   loopsSpacing: 0, // 环间距
//   angleIncrement: 0, // 填充角度增量
//   processTimes: 1, // 加工次数
// }

// optimizeParams = {
//   useOptimize: true, // 是否使用优化
//   accDistance: 0.05, // 加速距离，用于消除标刻开始段的打点不均匀现象
//   endCompensation: 0.05, // 末点补偿
//   biOffset: 0.01, // 双向偏移， 解决双向填充偏移问题
//   // 匀速标刻参数
//   limitAngle: 91, // 极限角度
//   accTime: 100, // 加速时间
//   decTime: 100, // 减速时间
//   onLightDelay: 100, // 开光延迟
//   offLightDelay: 100, // 关光延迟
// }
/**
 * 
 * @param gcode 生成的 gcode 字符串数组
 * @param svgString svg 字符串
 * @param speedMax 打标激光功率最大值
 * @param feed 进给速度 mm/min, 实际使用将 feed * 60
 * @param toolDiameter 激光直径 (toolDiameter 和 DPI 给定其中一个即可)
//  * @param DPI toolDiameter = 25.4 / dpi。
 * @param canvasMatrix 默认 [0, 1, 1, 0, -50, -50]。
 * @param fillMode 0：不填充 1: 水平单向 2: 水平双向 3: 垂直单向 4: 垂直双向 5: 弓形 6: 回形填充 7: 螺旋填充 8: 费马螺旋填充 9: 对角线 10: 反对角线 11: 网格 12: 对角网格 13: 十字填充 14: 对角十字填充 15: 方形填充 16: 希尔伯特曲线
 * @param markMode 打标模式： 0：平面达标 1：扩副打标 2：旋转轴打标(抓盘) 3:旋转轴打标（滚轴）
 * @param previewMode -1 非预览  1- 区域预览 2-轮廓预览 3-旋转轴直线预览 4-旋转轴外框预览 
 * @param deviceConfig 设备配置参数，当前未处理改参数
 * @param cancelStroke 是否取消描边，布尔值，默认 false
 * @param isHandhoudMode 是否时手持模式，默认false
 * @param fillAngle 填充角度，默认 0
 * @param isRelativeCoord 是否使用相对坐标
 * @param ratateParams {diameter: number, perRevolutionPulsesClamp: number, perRevolutionPulsesRoller: number} 直径 每转脉冲数
 */
function svg2gcode(gcode,
  { svgString,
    gcodeDriver,
    speedMax,
    feed,
    toolDiameter,
    canvasMatrix,
    fillMode,
    markMode,
    previewMode,
    cancelStroke,
    fillAngle,
    isHandhoudMode,
    autoAngleIncrement,
    frequency,
    fillParams,
    optParams = {},
    rotateParams = {},
    isRelativeCoord = false, 
    gcodeFinishCmd = true,
    gcodeStartCmd = true },
    deviceConfig = {}) {

  // 1. 参数配置设置
  var gdopt = { // gcode 驱动参数
    swapXY : false, // 是否交换 XY 轴
    isRelativeCoord: false
  };

  if (markMode === 2 || markMode === 3) {
    gdopt.swapXY = true; // 圆柱打标交换 X Y 轴
    gdopt.isRelativeCoord = true; // 圆柱打标使用相对坐标
  }

  // 2. 初始化 gCode 生成器
  var gCodeDriver =gcodeDriver || new GCodeDriver({
    write: function (str) {
      gcode.push(str); // 将接收到的字符串写入 gcode 变量
    }
  }, gdopt);

  // 3. 画布数据设置
  var gctx = new GCanvas(gCodeDriver);
  if (canvasMatrix) {
    if(isHandhoudMode) canvasMatrix = canvasMatrix.map(function(x) { return -x; });;
    gctx.setTransform.apply(gctx, canvasMatrix);
  } else {
    if (isHandhoudMode) {
      gctx.setTransform(-1, 0, 0, 1, 50, -50);
    } else {
      gctx.setTransform(1, 0, 0, -1, -50, 50);
    }
    // gctx.setTransform(0, 1, 1, 0, -50, -50);
  }
  if (speedMax) gctx.setSpeedMax(speedMax);
  if (feed) {
    if (previewMode !== -1) gctx.setFeed(feed * 60);
    else gctx.setFeed(feed * 40);
  } 
  if (frequency) gctx.setFrequency(frequency);

  let optimizeParams = {};
  if (previewMode === -1 ) {
    if (feed < 2000) { 
      optimizeParams = Object.assign({
        useOptimize: false, // 是否使用优化
        onLightDelay: 90, // 开光延迟
        offLightDelay: 190, // 关光延迟
        jumpDelay: 300, // 跳转延迟
        cornerDelay: 80 // 拐角延迟
      }, optParams);
    } else if (feed >= 2000 && feed < 5000){
      optimizeParams = Object.assign({
        useOptimize: true, // 是否使用优化
        accDistance: (feed - 2000) * 0.00005 + 0.09, // 加速距离，用于消除标刻开始段的打点不均匀现象
        decDistance: (feed - 2000) * 0.00005 + 0.25, // 减速距离，用于消除标刻结束段的打点不均匀现象
        endCompensation: 0.05, // 末点补偿
        biOffset: 0.01, // 双向偏移， 解决双向填充偏移问题
        // 匀速标刻参数
        limitAngle: 91, // 极限角度
        accTime: 100, // 加速时间
        decTime: 100, // 减速时间
        onLightDelay: 0, // 开光延迟
        // onLightDelay: -220, // 开光延迟
        offLightDelay: 0, // 关光延迟
        jumpDelay: 300, // 跳转延迟
        cornerDelay: 0, // 拐角延迟
        minLength: feed * 0.00001 // 10us 一个指令 8000mm/s 
        }, optParams);
    } else{// 只有在高速打标时开启优化
      optimizeParams = Object.assign({
      useOptimize: true, // 是否使用优化
      accDistance: (feed - 5000) * 0.00005 + 0.25, // 加速距离，用于消除标刻开始段的打点不均匀现象
      decDistance: (feed - 5000) * 0.00005 + 0.6, // 减速距离，用于消除标刻结束段的打点不均匀现象
      endCompensation: 0.05, // 末点补偿
      biOffset: 0.01, // 双向偏移， 解决双向填充偏移问题
      // 匀速标刻参数
      limitAngle: 91, // 极限角度
      accTime: 100, // 加速时间
      decTime: 100, // 减速时间
      onLightDelay: 0, // 开光延迟
      // onLightDelay: -220, // 开光延迟
      offLightDelay: 0, // 关光延迟
      jumpDelay: 300, // 跳转延迟
      cornerDelay: 0, // 拐角延迟
      minLength: feed * 0.00001 // 10us 一个指令 8000mm/s 
      }, optParams);
    }
  } else {
    optimizeParams = Object.assign({
      useOptimize: false, // 是否使用优化
      onLightDelay: 90, // 开光延迟
      offLightDelay: 220, // 关光延迟
      jumpDelay: 300, // 跳转延迟
      cornerDelay: 100 // 拐角延迟
    }, optParams);
  }
  gctx.setOptimizeParams(optimizeParams); // 这一步要在 feed 后面

  if (fillParams && fillParams.useFill && previewMode === -1) {
    let fp = fillParams;

    fp = Object.assign({
      useFill: true,
      strokeFirst: true, // 轮廓优先，默认先加工轮廓，后加工填充
      style: fillMode || 2, // 填充 模式，2为水平双向
      aroundEdgeOnce: false, // 绕边走一次      
      crossFill: false, // 交叉填充
      angle: 0, // 填充角度
      lineSpacing: toolDiameter || 0.05, // 填充线间距
      evenDistribution: true, // 均匀分布填充线
      margins: 0, // 填充边距
      beginOffset: 0, // 开始偏移
      endOffset: 0, // 结束偏移
      lineIndent: 0, // 填充线缩进
      boundaryLoopsNum: 0, // 边界环数
      loopsSpacing: 0, // 环间距
      angleIncrement: 0, // 填充角度增量
      processTimes: 1, // 加工次数
    }, fp);



    if (fp.lineSpacing) gctx.toolDiameter = fp.lineSpacing;

    if (cancelStroke) {
      gctx.stroke = function () { };
    }
    if (fp.style === 0) {
      gctx.fill = function () { };
    } else {
      gctx.setFillStyle(fp.style);
    }


    gctx.motion.setPreviewMode(false);
    gctx.motion.initDriver();

    if (fp.processTimes > 1 && fp.angleIncrement && fp.angleIncrement != 0) {
      for (let i = 0; i < fp.processTimes; i++) {
        let newGctx = gctx.clone(gCodeDriver);
        newGctx.setFillAngle(fp.angle + i * fp.angleIncrement);
        if (cancelStroke) {
          newGctx.stroke = function () { };
        }
        if (fp.style === 0) {
          newGctx.fill = function () { };
        } else {
          newGctx.setFillStyle(fp.style);
        }
        if (speedMax) newGctx.setSpeedMax(speedMax);
        if (feed) newGctx.setFeed(feed * 40);
        newGctx.motion.setPreviewMode(false);
        canvg(newGctx.canvas, svgString);
      }
    } else {
      gctx.setFillAngle(fp.angle);
      canvg(gctx.canvas, svgString);
    }

  } else {
    // var gctx = new GCanvas(gCodeDriver);

    if (toolDiameter) gctx.toolDiameter = toolDiameter;
    // if (DPI) gctx.toolDiameter = 25.4 / DPI;

    if (cancelStroke) {
      gctx.stroke = function () { };
    }
    if (fillMode === 0) {
      gctx.fill = function () { };
    }
    if (fillMode) {
      gctx.setFillStyle(fillMode);
    }
    
    if (markMode) {
      if (markMode === 2) { // 处理旋转打标(抓盘)
        // 计算A轴步进角度
        let l = toolDiameter || 0.05; // 一次旋转的默认弧长
        let d = rotateParams.diameter; // 圆柱直径
        let p = 360 /rotateParams.perRevolutionPulsesClamp; // 每个脉冲数旋转角度

        let numPixelPulse = Math.round(l * 360 / (Math.PI * d) /  p) ;
        let aAxisStepAngle = Math.round(numPixelPulse * p * 1000) / 1000; // A轴步进角度
        let yAxisStepLength = numPixelPulse * p * Math.PI * d / 360; // Y轴步进真实长度
        let xAxisStepLength =  yAxisStepLength; // x轴步进真实长度

        let rp = {
          enable: true,
          previewMode: previewMode,
          aAxisStepAngle: aAxisStepAngle,
          yAxisStepLength: yAxisStepLength,
          xAxisStepLength: xAxisStepLength
        }

        gctx.setRotateMotion(rp);
      }else if (markMode === 3){ // 滚轴
        let l = toolDiameter || 0.05; // 一次旋转的默认弧长
        
        if (!rotateParams.transmissionRatio){
          if (rotateParams.perRevolutionPulsesClamp && rotateParams.perRevolutionPulsesRoller){
              rotateParams.transmissionRatio = rotateParams.perRevolutionPulsesClamp / rotateParams.perRevolutionPulsesRoller;
          }else{
              rotateParams.transmissionRatio = 4; 
          }
        } 
        let transRatio = rotateParams.transmissionRatio;
        let rd = rotateParams.rollerDiameter * transRatio; // 滚轴直径
        let p = 360 /rotateParams.perRevolutionPulsesClamp; // 每个脉冲数旋转角度
        let unitArcLength = Math.PI * rd / rotateParams.perRevolutionPulsesClamp; // 一个脉冲对应的弧长
        let numPixelPulse = Math.round( l / unitArcLength); // A 轴步进角度
        let aAxisStepAngle = Math.round(numPixelPulse * p * 1000) / 1000; // A轴步进角度
        let yAxisStepLength = numPixelPulse * p * Math.PI * rd / 360; // Y轴步进真实长度
        let xAxisStepLength =  yAxisStepLength; // x轴步进真实长度

        let rp = {
          enable: true,
          previewMode: previewMode,
          aAxisStepAngle: aAxisStepAngle,
          yAxisStepLength: yAxisStepLength,
          xAxisStepLength: xAxisStepLength 
        }
        gctx.setRotateMotion(rp);
      }

    }
    if (previewMode === -1) { // 正常打标模式
      gctx.motion.setPreviewMode(false);
      if(gcodeStartCmd) gctx.motion.initDriver();
      if (autoAngleIncrement && autoAngleIncrement != 0) {
        let processTimes = Math.floor(360 / autoAngleIncrement);
        for (let i = 1; i <= processTimes; i++) {
          let newGctx = gctx.clone(gCodeDriver);
          newGctx.setFillAngle(fillAngle + i * autoAngleIncrement);
          if (cancelStroke) {
            newGctx.stroke = function () { };
          }
          if (fillMode === 0) {
            newGctx.fill = function () { };
          }
          if (fillMode) {
            newGctx.setFillStyle(fillMode);
          }
          if (speedMax) newGctx.setSpeedMax(speedMax);
          if (feed) newGctx.setFeed(feed * 40);
          newGctx.setOptimizeParams(optimizeParams);
          newGctx.motion.setPreviewMode(false);
          canvg(newGctx.canvas, svgString);
        }
      } else {
        gctx.motion.setPreviewMode(false);
        if (fillAngle) {
          gctx.setFillAngle(fillAngle);
        } else {
          gctx.setFillAngle(0);
        }
        canvg(gctx.canvas, svgString);
      }
      // canvg(gctx.canvas, svgString);
    } else if (previewMode === 1 && markMode === 0) { // 平面区域预览模式
      // TODO: 区域预览模式
      gctx.motion.setPreviewMode(true);
      gctx.motion.initDriver();
      // 屏蔽 fill 和 stroke 路径。
      gctx.fill = function () { };
      gctx.stroke = function () { };
      canvg(gctx.canvas, svgString);
      gctx.drawBounds();
    } else if (previewMode === 2 && markMode === 0) { // 轮廓预览模式
      gctx.motion.setPreviewMode(true);
      gctx.motion.initDriver();
      gctx.fill = function () { };
      canvg(gctx.canvas, svgString);
    } else if (previewMode === 3 && (markMode === 2 || markMode === 3)) { // 旋转轴直线预览模式
      gctx.motion.setPreviewMode(true);
      gctx.motion.initDriver();
      gctx.fill = function () { };
      gctx.stroke = function () { };
      canvg(gctx.canvas, svgString);
      gctx.rotateModePreviewLine();
    } else if (previewMode === 4 && (markMode === 2 || markMode === 3)) { // 旋转轴外框预览模式
      gctx.motion.setPreviewMode(true);
      gctx.motion.initDriver();
      gctx.fill = function () { };
      gctx.stroke = function () { };
      canvg(gctx.canvas, svgString);
      gctx.rotateModePreviewRect();
    }
  }
  if(gcodeFinishCmd) gctx.motion.finishProgram();
  console.log('SVG to GCode All Done!')
  return gcode;
}
